﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace SocketServer
{
    public class SocketServer
    {
        #region VARIABLES & CONSTRUCTEURS

        private static SocketServer Self = null;
        private static readonly object mylock = new object();

        private TcpListener _serverSocket;
        private int _conPort = 8000;
        private static string _userName;
        private Thread _waitConnection;

        public delegate void RequestEventHandler(object sender, RequestEventArgs e);
        public event RequestEventHandler OnClientRequest;
        public delegate void SocketEventHandler(object sender, SocketEventArgs e);
        public event SocketEventHandler OnStateChange;
        public delegate void ClientMessageEventHandler(object sender, ClientMessageEventArgs e);
        public event ClientMessageEventHandler OnClientMessage;

        private SocketServer()
        {
            string message = "Connecté";

            try
            {
                //IPHostEntry hostInfo = Dns.GetHostEntry(Dns.GetHostName());
                IPHostEntry hostInfo = Dns.Resolve(Dns.GetHostName());
                IPAddress ipAddress = hostInfo.AddressList[0];
                IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, _conPort);

                //Création de la socket
                _serverSocket = new TcpListener(ipAddress, _conPort);
            }
            catch (SocketException ex)
            {
                message = ex.Message;
            }

            if (OnStateChange != null)
                OnStateChange(this, new SocketEventArgs(message));
        }

        /// <summary>
        /// Starts the specified user name.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        public static SocketServer GetInstance(string userName)
        {
            _userName = userName;

            lock (mylock)
            {
                if (Self == null)
                    Self = new SocketServer();
                return Self;
            }
        }

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public void Start()
        {
            try
            {
                _serverSocket.Start();
                if (OnStateChange != null)
                    OnStateChange(this, new SocketEventArgs("Connecté"));

                _waitConnection = new Thread(new ThreadStart(waitForClientConnection));
                _waitConnection.Start();
            }
            catch (SocketException)
            {
                if (OnStateChange != null)
                    OnStateChange(this, new SocketEventArgs("Connection impossible"));
            }
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public void Stop()
        {
            lock (mylock)
            {
                if (Self != null)
                {
                    _waitConnection.Join(100);
                    _serverSocket.Stop();
                }
            }
        }

        #endregion

        /// <summary>
        /// Waits for client connection.
        /// </summary>
        private static void waitForClientConnection()
        {
            while (Self._serverSocket != null)
            {
                Console.WriteLine("Attente d'une nouvelle connexion...");

                //L'exécution du thread courant est bloquée jusqu'à ce qu'un
                //nouveau client se connecte

                try
                {
                    TcpClient client = Self._serverSocket.AcceptTcpClient();
                    byte[] msg = new byte[client.Available];
                    client.Client.Receive(msg, msg.Length, SocketFlags.None);
                    String msgString = System.Text.Encoding.UTF8.GetString(msg);
                    string ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();

                    Console.WriteLine("Nouveau client: " + client.GetHashCode());
                    Console.WriteLine("Client IP: " + ip);

                    Self.OnClientRequest(Self, new RequestEventArgs(msgString, ip, client));

                    //Démarrage du thread
                    //Thread getReadClients = new Thread(new ParameterizedThreadStart(getRead));
                    //getReadClients.Start(new NetworkStream(client));
                }
                catch (Exception)
                {
                }

            }
        }

        /// <summary>
        /// STRs to byte array.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <returns></returns>
        private static byte[] StrToByteArray(string str)
        {
            System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
            return encoding.GetBytes(str);
        }
    }

    #region CUSTOM EVENTARGS

    public class RequestEventArgs : EventArgs
    {
        public string IP { get; private set; }
        public string ClientName { get; private set; }
        public TcpClient Socket { get; private set; }

        public RequestEventArgs(string name, string ip, TcpClient socket)
        {
            IP = ip;
            ClientName = name;
            Socket = socket;
        }
    }
    public class ClientMessageEventArgs : EventArgs
    {
        public int ClientHashCode { get; private set; }
        public string Message { get; private set; }

        public ClientMessageEventArgs(int clientPosition, string msg)
        {
            ClientHashCode = clientPosition;
            Message = msg;
        }
    }
    public class SocketEventArgs : EventArgs
    {
        public string Message { get; private set; }

        public SocketEventArgs(string msg)
        {
            this.Message = msg;
        }
    } 

    #endregion
}
